1 module hip.util.to_string_range; 2 import std.range.primitives; 3 import hip.util.reflection:isArray; 4 5 void put(Sink, E)(ref Sink sink, E e) 6 { 7 static if(is(E == U[], U)) 8 { 9 static if(__traits(hasMember, sink, "preAllocate")) 10 sink.preAllocate(e.length); 11 foreach(element; e) 12 sink.put(element); 13 } 14 else 15 sink.put(e); 16 } 17 18 void put(ref char[] sink, char ch) 19 { 20 sink[0] = ch; 21 sink = sink[1..$]; 22 } 23 24 void put(ref char[] sink, string str) 25 { 26 sink[0..str.length] = str; 27 sink = sink[str.length..$]; 28 } 29 30 31 void toStringRange(Sink, Enum)(ref Sink sink, Enum enumMember) if(is(Enum == enum)) 32 { 33 foreach(mem; __traits(allMembers, Enum)) 34 if(__traits(getMember, Enum, mem) == enumMember) 35 { 36 put(sink, Enum.stringof ~ "." ~ mem); //@nogc string, resolved at compile time 37 return; 38 } 39 put(sink, Enum.stringof ~ ".|MEMBER_NOT_FOUND|"); //@nogc string, resolved at compile time 40 } 41 42 void toStringRange(Sink)(ref Sink sink, float f) 43 if(isOutputRange!(Sink, char)) 44 { 45 if(f != f) //nan 46 return put(sink, "nan"); 47 else if(f == -float.infinity) 48 return put(sink, "-inf"); 49 if(f == float.infinity) 50 return put(sink, "inf"); 51 if(f < 0) 52 { 53 f = -f; 54 put(sink, '-'); 55 } 56 57 float decimal = f - cast(int)f; 58 toStringRange(sink, cast(int)f); 59 if(decimal == 0) 60 return; 61 put(sink, '.'); 62 long multiplier = 10; 63 while(cast(long)(decimal*multiplier) < (decimal*multiplier)) 64 { 65 if(cast(long)(decimal*multiplier) == 0) 66 put(sink, '0'); 67 multiplier*=10; 68 } 69 toStringRange(sink, (cast(long)(decimal*multiplier))); 70 } 71 72 73 void toStringRange(Sink, T)(ref Sink sink, T[] arr) 74 if(isOutputRange!(Sink, char) && !is(T[] == string) && !is(T[] == char[])) //There is a better match for char/string 75 { 76 static if(__traits(compiles, sink.preAllocate)) 77 { 78 //2: '[' and ']' 79 // 2 * arr.length: ", " (no need to use - 1 as there will be the inputs) 80 sink.preAllocate(2 + 2 * arr.length); 81 } 82 put(sink, '['); 83 for(int i = 0; i < arr.length; i++) 84 { 85 if(i != 0) 86 { 87 foreach(character; ", ") 88 put(sink, character); 89 } 90 toStringRange(sink, arr[i]); 91 } 92 put(sink, ']'); 93 } 94 95 96 void toStringRange(Sink, T)(ref Sink sink, T structOrTupleOrClass) 97 if(!isArray!T && (is(T == struct) || is(T == class) || is(T == interface))) 98 { 99 static if(is(T == struct))//For structs declaration 100 { 101 import hip.util.reflection; 102 alias struct_ = structOrTupleOrClass; 103 static if(__traits(hasMember, T, "toString") && hasUDA!(__traits(getMember, T, "toString"), "format")) 104 { 105 import hip.util.format; 106 formatFromType(sink, struct_); 107 } 108 else 109 { 110 put(sink, T.stringof); 111 put(sink, '('); 112 foreach(i, v; struct_.tupleof) 113 { 114 if(i > 0) 115 put(sink, ", "); 116 toStringRange(sink, v); 117 } 118 put(sink, ')'); 119 } 120 } 121 else static if(is(T == class)) 122 { 123 alias class_ = structOrTupleOrClass; 124 put(sink, T.classinfo.name); 125 put(sink, '('); 126 foreach(i, v; class_.tupleof) 127 { 128 if(i > 0) 129 put(sink, ", "); 130 toStringRange(sink, v); 131 } 132 put(sink, ')'); 133 } 134 else static if(is(T == interface)) 135 { 136 put(sink, T.classinfo.name); 137 } 138 // else static if(isTuple!T) 139 // { 140 // alias tupl = structOrTupleOrClass; 141 // put(sink, T.stringof); 142 // put(sink, '('); 143 // foreach(i, v; tupl) 144 // { 145 // if(i > 0) 146 // put(sink, ", "); 147 // toStringRange(sink, v); 148 // } 149 // put(sink, ')'); 150 // } 151 else static assert(0, "Not implemented for "~T.stringof); 152 } 153 154 // void toStringRange(Sink)(auto ref Sink sink, scope const char[] arr) if(isOutputRange!(Sink, char)) 155 // { 156 // static if(__traits(compiles, sink.preAllocate)) 157 // sink.preAllocate(arr.length); 158 // foreach(ch; arr) 159 // put(sink, ch); 160 // } 161 162 void toStringRange(Sink)(ref Sink sink, string str) if(isOutputRange!(Sink, char)) 163 { 164 static if(__traits(compiles, sink.preAllocate)) 165 sink.preAllocate(str.length); 166 foreach(character; str) 167 put(sink, character); 168 } 169 170 void toStringRange(Sink)(ref Sink sink, const(char)* str) if(isOutputRange!(Sink, char)) 171 { 172 import core.stdc.string:strlen; 173 size_t length = strlen(str); 174 static if(__traits(compiles, sink.preAllocate)) 175 sink.preAllocate(length); 176 for(int i = 0; i < length; i++) 177 put(sink, str[i]); 178 } 179 180 void toStringRange(Sink)(ref Sink sink, const(ubyte)* str) if(isOutputRange!(Sink, char)) 181 { 182 toStringRange(sink, cast(const(char)*)str); 183 } 184 185 void toStringRange(Sink)(ref Sink sink, void* ptr) if(isOutputRange!(Sink, char)) 186 { 187 if(ptr is null) 188 put(sink, "null"); 189 else 190 toHex(sink, cast(size_t)ptr); 191 } 192 193 void toStringRange(Sink)(ref Sink sink, bool b) if(isOutputRange!(Sink, char)) 194 { 195 put(sink, b ? "true" :"false"); 196 } 197 198 void toStringRange(Sink)(ref Sink sink, long x) 199 if(isOutputRange!(Sink, char)) 200 { 201 enum numbers = "0123456789"; 202 int preAllocSize = 0; 203 bool isNegative = x < 0; 204 if(isNegative) 205 { 206 x*= -1; 207 preAllocSize++; 208 } 209 ulong div = 10; 210 while(div <= x) 211 { 212 div*=10; 213 preAllocSize++; 214 } 215 div/= 10; 216 static if(__traits(hasMember, sink, "preAllocate")) 217 sink.preAllocate(preAllocSize); 218 if(isNegative) 219 put(sink, '-'); 220 while(div >= 10) 221 { 222 put(sink, numbers[(x/div)%10]); 223 div/=10; 224 } 225 put(sink, numbers[cast(size_t)(x%10)]); 226 } 227 228 229 void toHex(Sink)(ref Sink sink, size_t n) 230 if(isOutputRange!(Sink, char)) 231 { 232 enum numbers = "0123456789ABCDEF"; 233 int preAllocSize = 1; 234 ulong div = 16; 235 while(div <= n) 236 { 237 div*= 16; 238 preAllocSize++; 239 } 240 div/= 16; 241 static if(__traits(hasMember, sink, "preAllocate")) 242 sink.preAllocate(preAllocSize); 243 244 while(div >= 16) 245 { 246 put(sink, numbers[(n/div)%16]); 247 div/= 16; 248 } 249 put(sink, numbers[n%16]); 250 }